package com.lcc.lynx.jdbc.qw;

import com.lcc.lynx.common.exception.e.JDBCException;
import com.lcc.lynx.common.utils.SpringUtil;
import com.lcc.lynx.jdbc.enums.QwMethod;
import com.lcc.lynx.jdbc.mapper.ExecuteSqlMapper;
import com.lcc.lynx.jdbc.util.JDBCReflectUtil;
import lombok.Data;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 查询sql构造器
 *
 * @author lcc
 * @date 2025/7/23 13:45
 */
@Data
public class QueryWrapper {

    private static final String SELECT = "select ";
    private static final String FROM = " from ";
    private static final String WHERE = " where ";
    private static final String AND = " and ";
    private static final String OR = " or ";
    private static final String ON = " on ";
    private static final String AS = " as ";
    private static final String ORDER_BY = " order by ";
    private static final String GROUP_BY = " group by ";
    private static final String LIMIT = " limit ";
    private static final String OFFSET = " offset ";
    private static final String HAVING = " having ";
    private static final String UNION_ALL = " union all ";


    private StringBuilder sql = new StringBuilder();
    private Map<String, Object> params = new HashMap<>();
    private Map<String, String> tableAsMap = new HashMap<>();
    private Map<String, String> parentAsMap = null;
    private List<TableDef> tableDefList = new ArrayList<>();
    private List<TableJoin> tableJoinList = new ArrayList<>();
    private List<QueryCondition> andQueryConditionList = new ArrayList<>();
    private List<QueryCondition> orQueryConditionList = new ArrayList<>();
    private List<QueryCondition> havingQueryConditionList = null;
    // 递归时 子查询条件
    private List<QueryCondition> sonQueryConditionList = null;
    private List<QueryWrapper> unionAllList = null;

    private List<Column> columnList;
    private List<Column> orderByList;
    private List<Column> groupByList;
    private Class<?> resultType;
    private String limit = null;
    private Column distinct = null;
    private String methodName = null;

    public QueryWrapper() {

    }

    public QueryWrapper(String sql) {
        this.sql.append(sql);
    }

    public static QueryWrapper resultType(Class<?> resultType) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.setResultType(resultType);
        queryWrapper.printMethodName();
        return queryWrapper;
    }

    public QueryWrapper select(Column... columns) {
        columnList = Arrays.stream(columns).map(item -> {
            if (item.getTableDef() != null) {
                if (!tableDefList.contains(item.getTableDef())) {
                    tableDefList.add(item.getTableDef());
                }
            }
            if (item instanceof ColumnCondition c) {
                if (c.getQwMethod() == QwMethod.DISTINCT || c.getQwMethod() == QwMethod.DISTINCT_COLUMN) {
                    distinct = c;
                    return null;
                }
            }
            return item;
        }).filter(Objects::nonNull).toList();
        return this;
    }

    public QueryWrapper from(Class<? extends TableDef>... tableDefClass) {
        for (Class<? extends TableDef> defClass : tableDefClass) {
            TableDef tableDefs = JDBCReflectUtil.getTableDef(defClass);
            tableJoinList.add(new TableJoin(tableDefs, TableJoin.DEFAULT_JOIN));
        }
        return this;
    }

    public QueryWrapper as(String as) {
        TableJoin tableJoin = tableJoinList.get(tableJoinList.size() - 1);
        tableJoin.setAs(as);
        if (tableJoin.getTableName() != null) {
            tableAsMap.putIfAbsent(tableJoin.getTableName(), as);
        }
        return this;
    }

    public QueryWrapper leftJoin(Class<? extends TableDef> tableDefClass) {
        TableDef tableDefs = JDBCReflectUtil.getTableDef(tableDefClass);
        tableJoinList.add(new TableJoin(tableDefs, TableJoin.LEFT_JOIN));
        return this;
    }

    public QueryWrapper leftJoin(QueryWrapper queryWrapper) {
        tableJoinList.add(new TableJoin(queryWrapper, TableJoin.LEFT_JOIN));
        return this;
    }

    public QueryWrapper innerJoin(Class<? extends TableDef> tableDefClass) {
        TableDef tableDefs = JDBCReflectUtil.getTableDef(tableDefClass);
        tableJoinList.add(new TableJoin(tableDefs, TableJoin.INNER_JOIN));
        return this;
    }

    public QueryWrapper innerJoin(QueryWrapper queryWrapper) {
        tableJoinList.add(new TableJoin(queryWrapper, TableJoin.INNER_JOIN));
        return this;
    }

    public QueryWrapper rightJoin(Class<? extends TableDef> tableDefClass) {
        TableDef tableDefs = JDBCReflectUtil.getTableDef(tableDefClass);
        tableJoinList.add(new TableJoin(tableDefs, TableJoin.RIGHT_JOIN));
        return this;
    }

    public QueryWrapper rightJoin(QueryWrapper queryWrapper) {
        tableJoinList.add(new TableJoin(queryWrapper, TableJoin.RIGHT_JOIN));
        return this;
    }

    public QueryWrapper on(QueryCondition... queryConditions) {
        tableJoinList.get(tableJoinList.size() - 1).setJoinQueryConditionList(List.of(queryConditions));
        return this;
    }

    public QueryWrapper where(QueryCondition... queryConditions) {
        andQueryConditionList.addAll(Stream.of(queryConditions)
                .filter(Objects::nonNull)
                .filter(QueryCondition::isNotEmpty)
                .toList());
        return this;
    }

    /**
     * 添加递归子查询条件
     *
     * @param queryConditions
     * @return
     * @author lcc
     * @date 2025/7/29 12:08
     */
    public QueryWrapper sonWhere(QueryCondition... queryConditions) {
        if (sonQueryConditionList == null) {
            sonQueryConditionList = new ArrayList<>();
        }
        sonQueryConditionList.addAll(Stream.of(queryConditions)
                .filter(Objects::nonNull)
                .filter(QueryCondition::isNotEmpty)
                .toList());
        return this;
    }

    public QueryWrapper and(QueryCondition queryCondition) {
        andQueryConditionList.add(queryCondition);
        return this;
    }

    public QueryWrapper and(boolean test, QueryCondition queryCondition) {
        if (test) {
            andQueryConditionList.add(queryCondition);
        }
        return this;
    }

    public QueryWrapper or(QueryCondition queryCondition) {
        orQueryConditionList.add(queryCondition);
        return this;
    }

    public QueryWrapper unionAll(QueryWrapper queryWrapper) {
        if (unionAllList == null) {
            unionAllList = new ArrayList<>();
        }
        queryWrapper.setParams(this.params);
        unionAllList.add(queryWrapper);
        return this;
    }

    public QueryWrapper orderBy(ColumnCondition... queryConditions) {
        orderByList = List.of(queryConditions);
        return this;
    }

    public QueryWrapper groupBy(ColumnCondition... columns) {
        groupByList = List.of(columns);
        return this;
    }

    public QueryWrapper having(QueryCondition... queryConditions) {
        havingQueryConditionList = List.of(queryConditions);
        return this;
    }

    public QueryWrapper limit(int offset, int limit) {
        this.limit = OFFSET + offset + LIMIT + limit;
        return this;
    }

    public QueryWrapper limit(int limit) {
        this.limit = LIMIT + limit;
        return this;
    }

    public String buildSql(QueryWrapper parentQueryWrapper) {
        this.setParentAsMap(parentQueryWrapper.getTableAsMap());
        this.setParams(parentQueryWrapper.getParams());
        return this.buildSql();
    }

    public String buildSql() {
        if (sql.length() > 0) {
            return sql.toString();
        }
        if (methodName != null) {
            sql.append("/* ").append(methodName).append(" */ ");
        }
        if (unionAllList != null && (orderByList != null || groupByList != null)) {
            sql.append("(");
        }
        if (tableJoinList.isEmpty() && !tableDefList.isEmpty()) {
            tableJoinList.addAll(tableDefList.stream().map(TableJoin::new).toList());
        }
        if (tableJoinList.isEmpty()) {
            throw new JDBCException("请指定表");
        }
        sql.append(SELECT)
                .append(distinct != null ? distinct.build(this) + " " : "")
                .append(columnList.stream()
                        .map(item -> item.build(this))
                        .collect(Collectors.joining(", ")))
                .append(FROM);
        for (int i = 0; i < tableJoinList.size(); i++) {
            TableJoin tableJoin = tableJoinList.get(i);
            if (i > 0) {
                sql.append(tableJoin.getJoinType());
            }
            String as = tableJoin.getAs();
            if (as == null) {
                if (tableJoin.getTableDef() == null) {
                    throw new JDBCException("请为临时表指定表别名");
                } else {
                    String tableName = tableJoin.getTableName();
                    as = tableAsMap.get(tableName);
                }
            }
            if (tableJoin.getQueryWrapper() != null) {
                sql.append("(").append(tableJoin.getQueryWrapper().buildSql(this)).append(")");
            } else {
                sql.append(tableJoin.getTableName());
            }

            if (as != null && !as.equals(tableJoin.getTableName())) {
                sql.append(AS).append(as);
            }
            if (tableJoin.getJoinQueryConditionList() != null) {
                sql.append(ON).append(this.buildConditions(tableJoin.getJoinQueryConditionList(), AND));
            }
        }
        if (andQueryConditionList.size() > 0) {
            sql.append(WHERE)
                    .append(this.buildConditions(andQueryConditionList, AND))
                    .append(this.buildConditions(orQueryConditionList, OR));
        }
        if (groupByList != null) {
            sql.append(GROUP_BY).append(groupByList.stream()
                    .map(item -> item.build(this))
                    .collect(Collectors.joining(", ")));
        }
        if (havingQueryConditionList != null) {
            sql.append(HAVING).append(this.buildConditions(havingQueryConditionList, AND));
        }
        if (orderByList != null) {
            sql.append(ORDER_BY).append(orderByList.stream()
                    .map(item -> item.build(this))
                    .collect(Collectors.joining(", ")));
        }
        if (limit != null) {
            sql.append(limit);
        }
        if (unionAllList != null) {
            for (QueryWrapper queryWrapper : unionAllList) {
                if (orderByList != null || groupByList != null) {
                    sql.append(" ) ").append(UNION_ALL).append(" ( ");
                } else {
                    sql.append(UNION_ALL);
                }
                sql.append(queryWrapper.buildSql());
            }
            if (orderByList != null || groupByList != null) {
                sql.append(") ");
            }
        }

        return sql.toString();
    }


    private String buildConditions(List<QueryCondition> list, String joining) {
        return list.stream()
                .map(item -> item.build(this))
                .collect(Collectors.joining(joining));
    }

    public <R> List<R> list(Class<R> resultType) {
        ExecuteSqlMapper bean = SpringUtil.getBean(ExecuteSqlMapper.class);
        MyResultHandler<R> rMyResultHandler = new MyResultHandler<>(resultType);
        bean.list(this.buildSql(), params, rMyResultHandler);
        return rMyResultHandler.getResults();
    }

    public <R> List<R> list() {
        return (List<R>) this.list(this.getResultType());
    }

    public <R> R get(Class<R> resultType) {
        if(this.limit == null){
            this.limit(2);
        }
        List<R> list = this.list(resultType);
        if (list.isEmpty()) {
            return null;
        } else if (list.size() > 1) {
            throw new JDBCException("查询结果大于1条");
        } else {
            return list.get(0);
        }
    }

    public <R> R get() {
        return (R) this.get(this.getResultType());
    }


    public Class<?> getResultType() {
        if (resultType != null) {
            return resultType;
        } else if (tableDefList.size() > 0) {
            return tableDefList.get(0).getDvClass();
        } else if (tableJoinList.size() > 0) {
            return tableJoinList.get(0).getTableDef().getDvClass();
        } else {
            return Map.class;
        }
    }

    public QueryWrapper printMethodName() {
        if (methodName != null) return this;
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        // stackTrace[0] 是 getStackTrace() 方法本身，stackTrace[1] 是当前方法，所以从 stackTrace[2] 开始是调用者信息
        if (stackTrace.length > 3) {
            StackTraceElement caller = stackTrace[3]; // 获取调用当前方法的上层方法的信息
            methodName = caller.getClassName() + "." + caller.getMethodName();
        }
        return this;
    }
}
